package com.klangner.go.app.rules;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import com.klangner.go.model.IBoard;
import com.klangner.go.model.Move;
import com.klangner.go.model.impl.GoBoard;

/**
 * Represents board
 */
public class BoardModel implements Cloneable{

	/**
	 * Constructor
	 * @param size board size
	 */
	public BoardModel (int size) {

		this.size = size;

    goban = new GobanLocation[size][size];

    blackGroups = new Vector<StonesGroupOld>();
    whiteGroups = new Vector<StonesGroupOld>();
    setGoban(size);
	}
	
	public IBoard getBoard(){
		GoBoard board = new GoBoard(size);
		
    for (int i = 0; i < size; i++)
      for (int k = 0; k < size; k++) {
          board.setStone(i, k, goban[i][k].state);
      }
		
		return board;
	}
	
	
	/**
	 * @see IBoard#getStone(int, int)
	 */
	public Move getLastMove(){

		if (history.size() > 0) {
			return (Move)history.lastElement();
    } else {
  	  return null;
    }
	}
	
	/**
	 * @see IBoard#getStone(int, int)
	 */
	public int getStoneColor(int x, int y){
		assert(x > 0 && x < size);
		assert(y > 0 && y < size);
		
		return goban[x][y].state;
	}

    public Object cloneCopy() throws CloneNotSupportedException {

        GobanLocation currentPoint;
        BoardModel clonedGoPosition = new BoardModel(size);

        for (int i = 0; i < history.size(); ++i)
	    {
            Move move = (Move)history.elementAt( i );
            clonedGoPosition.makeMove(move.getX(), move.getY(),
                                        move.getColor());
        }

        clonedGoPosition.nrBlackPrisoners = this.nrBlackPrisoners;
        clonedGoPosition.nrWhitePrisoners = this.nrWhitePrisoners;

        //if (Debug.isDebugOn()) Debug.assert((this.history_.equals(clonedGoPosition.getHistory())), "GoPosition.cloneCopy failed to provide a good copy: original = " + this + " copy = " + clonedGoPosition);

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        //if (Debug.isDebugOn()) Debug.assert(clonedGoPosition.isConsistent(), "ERROR: GoPosition clone consistency check");

        //if (Debug.isDebugOn()) Debug.assert(this.sameAs(clonedGoPosition), "cloneCopy error");
        //if (Debug.isDebugOn()) Debug.assert(clonedGoPosition.sameAs(this), "cloneCopy error");

        return clonedGoPosition;
    }

        // tells if a move is legal or not
    public boolean isLegalMove(Move move) {

	    if (getLastMove() != null &&
            getLastMove().getColor() == move.getColor()) {
            System.err.println("ERROR in GoPostion.isLegelMove: one cannot move 2 times in a row");
	        System.exit( 1 );
		}

        if (getLastMove() == null && move.getColor() != IBoard.BLACK_STONE)
        {
            System.err.println("ERROR in GoPostion.isLegelMove: Black moves first");
            System.exit( 1 );
        }


		int x, y, color;

        x = move.getX();
        y = move.getY();
        color = move.getColor();

            // Accept PASS move
        if (move.isPass()) {
            return true;
        }

        boolean offBoard = (x < 0 || x >= size || y < 0 || y >= size);
        assert( !offBoard ); // "Move off board"
        
        if (offBoard)
            return false;

        if (goban[x][y].state != IBoard.NO_STONE)
            return false;

        GobanLocation lastMoveLocation = new GobanLocation(x, y, color, this);

            // if move has at least one free liberty, it's legal
        if (countLiberties(x, y) > 0)
            return true;

        Vector<StonesGroupOld> listOfNeighborFriends = new Vector<StonesGroupOld>();
        Vector<StonesGroupOld> listOfNeighborEnemies = new Vector<StonesGroupOld>();

            // fill the lists with new stone's friend and enemy groups
        visitNewStoneNeighbors(lastMoveLocation,
                               listOfNeighborFriends,
                               listOfNeighborEnemies);

            // check if we would capture anything
        Enumeration enemies = listOfNeighborEnemies.elements();
        for (;enemies.hasMoreElements();) {
            if (((StonesGroupOld)enemies.nextElement()).getLiberties().size() <= 1)
                return true;
        }

            // check if we commit suicide by this move
        int newOwnGroupNrLiberties = 0;

        Enumeration friends = listOfNeighborFriends.elements();
        for (;friends.hasMoreElements();) {
            newOwnGroupNrLiberties += ((StonesGroupOld)friends.nextElement()).getLiberties().size();
            newOwnGroupNrLiberties -= 1;
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");

        if (newOwnGroupNrLiberties > 0) return true;
        else return false;
    }

    public boolean isSelfAtariMove(Move lastMove){
        int x, y, color;

        x = lastMove.getX();
        y = lastMove.getY();
        color = lastMove.getColor();

        GobanLocation lastMoveLocation = new GobanLocation(x, y, color, this);

            // if lastMove has at least one free liberty, it's legal
        if (countLiberties(x, y) > 1)
            return false;

        Vector<StonesGroupOld> listOfNeighborFriends = new Vector<StonesGroupOld>();
        Vector<StonesGroupOld> listOfNeighborEnemies = new Vector<StonesGroupOld>();

            // fill the lists with new stone's friend and enemy groups
        visitNewStoneNeighbors(lastMoveLocation,
                               listOfNeighborFriends,
                               listOfNeighborEnemies);

            // check if we would capture anything
        Enumeration enemies = listOfNeighborEnemies.elements();
        for (;enemies.hasMoreElements();) {
            if (((StonesGroupOld)enemies.nextElement()).getLiberties().size() <= 1)
                return false;
        }

            // check if we commit suicide by this move
        int newOwnGroupNrLiberties = 0;

        Enumeration friends = listOfNeighborFriends.elements();
        for (;friends.hasMoreElements();) {
            newOwnGroupNrLiberties += ((StonesGroupOld)friends.nextElement()).getLiberties().size();
            newOwnGroupNrLiberties -= 1;
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");

        if (newOwnGroupNrLiberties > 1) return false;
        else return true;
    }

    public void setSize(int Size) {
        size = Size;

        goban = new GobanLocation[size][size];

        setGoban(size);

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    public int getSize() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return size;
    }
    
    /**
     * This function doesn't check if move is valid. 
     * It can replace stone and make suicide move. 
     * Please check this move according to game rules before making move
     * 
     * Add stone at specific location
     */
    public Vector<GobanLocation> makeMove(int x, int y, int color) {

        goban[x][y].state = color;
        Move move = new Move(x, y, color);

        computeLiberties(goban[x][y]);

            // build helping lists
        Vector<StonesGroupOld> listOfNeighborFriends = new Vector<StonesGroupOld>();

        Vector<StonesGroupOld> listOfNeighborEnemies = new Vector<StonesGroupOld>();

            // fill the lists with new stone's friend and enemy groups
        visitNewStoneNeighbors(goban[x][y],
                               listOfNeighborFriends,
                               listOfNeighborEnemies);

            //once we placed a move, it's location is no longer
            // a liberty for the surrounding friend and enemy groups
        removeNeighborsLiberty(goban[x][y],
                               listOfNeighborFriends,
                               listOfNeighborEnemies);

            // update the situation of the friend groups (listOfNeighborFriends) after the last move (stone)
        updateStonesFriendGroups(goban[x][y], listOfNeighborFriends);

            // take care of captured stones
        Vector<GobanLocation> changedLocations = updateStonesEnemyGroups(
            //goban[x][y],
            listOfNeighborEnemies);

        addToHistory(move);

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check on exiting setMove");

        return changedLocations;
    }

        // returns a new GoPosition, resulted after playing <stone>
    public BoardModel positionAfterMove(Move stone) {
        //if (Debug.isDebugOn()) Debug.assert(isLegalMove( stone ), "positionAfterMove called with wrong argument: " + stone + " in position: " + this);

        BoardModel newPosition = null;
        try {
            newPosition = (BoardModel)this.cloneCopy();

                // the position will not be modified in case of a PASS move!
            if (!stone.isPass()) {
//                newPosition.goban[stone.x][stone.y].state = stone.color;

//                Debug.log("setNewMove called from GoPosition.positionAfterMove, on " + newPosition);
                newPosition.makeMove(stone.getX(), stone.getY(), stone.getColor());
            }
        }
        catch (CloneNotSupportedException ex) {
            System.out.println("Problems in GoPosition.positionAfterMove: " + ex);
            System.exit(1);
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        //if (Debug.isDebugOn()) Debug.assert(newPosition.isConsistent(), "ERROR: GoPosition positionAfterMove consistency check");

        return newPosition;
    }

    public Iterator<Move> legalMoves() {
        Vector<Move> moves = new Vector<Move>();
        Move currentMove;
        int currentColor = getTurnColor();

        for (int i = 0; i < size; i++)
            for (int j = 0; j < size; j++) {
                currentMove = new Move(i, j, currentColor);
                if (isLegalMove(currentMove))
                    moves.addElement(currentMove);
            }

            // a PASS move is always legal ... :-)
            //moves.addElement(new Move(GoConstants.PASS,
            //                        GoConstants.PASS,
            //                      currentColor));

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");

        return moves.iterator();
    }

    public Vector<StonesGroupOld> getBlackGroups() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return blackGroups;
    }

    public Vector<StonesGroupOld> getWhiteGroups() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return whiteGroups;
    }

    public void setBlackGroups(Vector<StonesGroupOld> newBlackGroups) {
        blackGroups = newBlackGroups;
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    public void setWhiteGroups(Vector<StonesGroupOld> newWhiteGroups) {
        whiteGroups = newWhiteGroups;
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }


    public boolean isEmpty(int x, int y) {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return (goban[x][y].state == IBoard.NO_STONE);
    }

    public String toString() {
            //return "{B=" + blackGroups + "; W=" + whiteGroups + "}";
        StringBuffer result = new StringBuffer();

        result.append('\n');
        for (int i = 0; i < history.size(); ++i) {
            Move move = (Move)history.elementAt( i );
            result.append("(" + move.getX() + ", " + move.getY() +
                          " : " + move.getColor() + ") ");
        }
        result.append("\n");

        for ( int x = 0; x < size; ++x) {
            for ( int y = 0; y < size; ++y ) {
                GobanLocation location = goban[y][x];
                if ( location.state == IBoard.NO_STONE ) {
                    result.append('.');
                }
                else if ( location.state == IBoard.BLACK_STONE ) {
                    result.append('#');
                }
                else {
                    result.append('O');
                }
            }
            result.append('\n');
        }

        return new String( result );
    }

        // fast method to count liberties for the (x, y) goban location
    public int countLiberties(int x, int y) {
        int count = 0;

        if (x > 0)
            if (goban[x-1][y].state == IBoard.NO_STONE) count++;

        if (x < size - 1)
            if (goban[x+1][y].state == IBoard.NO_STONE) count++;

        if (y > 0)
            if (goban[x][y-1].state == IBoard.NO_STONE) count++;

        if (y < size - 1)
            if (goban[x][y+1].state == IBoard.NO_STONE) count++;

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");

        return count;
    }

    public int getTurnColor() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");

        if (history.size() == 0)
            return IBoard.BLACK_STONE;

        if (getLastMove().getColor() == IBoard.BLACK_STONE)
            return IBoard.WHITE_STONE;
        else
            return IBoard.BLACK_STONE;
    }

    public int getNrBlackPrisoners() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return nrBlackPrisoners;
    }

    public int getNrWhitePrisoners() {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        return nrWhitePrisoners;
    }

        // todo !!! improve this, is too slow for big boards
        // compare two board positions
    public boolean sameAs(BoardModel somePosition) {
        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
        //if (Debug.isDebugOn()) Debug.assert(somePosition.isConsistent(), "ERROR: GoPosition sameAs consistency check");

        for (int i = 0; i < size; i++)
            for (int k = 0; k < size; k++)
                if (goban[i][k].state != somePosition.goban[i][k].state)
                    return false;

        if (history.size() != somePosition.getHistory().size())
            return false;

        for (int i = 0; i < history.size(); ++i) {
            if (!history.elementAt(i).equals(somePosition.getHistory().elementAt(i)))
                return false;
        }

        return true;
    }

    private void setGoban(int Size) {
        size = Size;

        history = new Vector();

        for (int i = 0; i < size; i++)
            for (int k = 0; k < size; k++) {
                goban[i][k] = new GobanLocation(i, k, IBoard.NO_STONE, this);
            }

        for (int i = 0; i < size; i++) {
            for (int k = 0; k < size; k++) {
                    //goban[i][k].computeLiberties();
                    // This is an improvement over the above one: takes advantage of the
                    // knowledge that the goban is empty.
                setEmptyGobanLiberties(goban[i][k]);
            }
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

        // implementation procedure used in setNextMove
        // process the "stone"'s neighbor location ("currentX","currentY")
    private void processNeighbor(GobanLocation stone,
                                 int currentX, int currentY,
                                 Vector<StonesGroupOld> listOfNeighborFriends,
                                 Vector<StonesGroupOld> listOfNeighborEnemies) {

            // the currrently searched location is empty
        if (goban[currentX][currentY].state == IBoard.NO_STONE) {
            stone.getLiberties().add(goban[currentX][currentY]);
            goban[currentX][currentY].getLiberties().remove(stone);
            return;
        }

            // the currrently searched location is occupied by a friend;
            // include that stone's group in the listOfNeighborFriends
        else if (goban[currentX][currentY].state == stone.state) {
            listOfNeighborFriends.add(goban[currentX][currentY].getGroup());
        }
            // the currrently searched location is occupied by an enemy
            // include that stone's group in the listOfNeighborEnemies
        else {
            listOfNeighborEnemies.add(goban[currentX][currentY].getGroup());
        }
    }

        //once we placed a move, it's location is no longer
        // a liberty for the surrounding friend stones
    private void removeNeighborsLiberty(GobanLocation stone,
    		Vector<StonesGroupOld> listOfNeighborFriends,
    		Vector<StonesGroupOld> listOfNeighborEnemies) {

        //if (Debug.isDebugOn()) Debug.assert( (listOfNeighborFriends != null), "listOfNeighborFriends null");
        //if (Debug.isDebugOn()) Debug.assert( (listOfNeighborEnemies != null), "listOfNeighborEnemies null");

        Enumeration e = listOfNeighborFriends.elements();
        for (;e.hasMoreElements();) {
            StonesGroupOld currentGroup = (StonesGroupOld)e.nextElement();
            //if (Debug.isDebugOn()) Debug.assert( (currentGroup != null), "friend group null in position:" + this.toString());

            currentGroup.getLiberties().remove(stone);
        }

            //once we placed a move, it's location is no longer
            // a liberty for the surrounding enemy stones
        e = listOfNeighborEnemies.elements();
        for (;e.hasMoreElements();) {
            StonesGroupOld currentGroup = (StonesGroupOld)e.nextElement();
            //if (Debug.isDebugOn()) Debug.assert( (currentGroup != null), "enemy group null in position:" + this.toString());

            currentGroup.getLiberties().remove(stone);
        }
    }

        // update the situation of the friend groups (listOfNeighborFriends) after the last move (stone)
    private void updateStonesFriendGroups(GobanLocation stone,
    		Vector<StonesGroupOld> listOfNeighborFriends) {
            // no friends around...means a new group has been born!
        if (listOfNeighborFriends.isEmpty())
                // build a new group that contains only one stone
            newBornGroup(stone);

            //or exactly one friend...
        else if (listOfNeighborFriends.size() == 1)
                // the last move (stone) should be appended to the only neighbor friend group
            addStoneToGroup(stone,
                            listOfNeighborFriends.firstElement());

            //or more than one friend
        else
                // the last move (stone) is connected with several friendly groups (listOfNeighborFriends)
            connectMoreGroups(stone, listOfNeighborFriends);

//        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }



        // update the situation of the enemy groups (listOfNeighborEnemies) after the last move (stone)
    private Vector<GobanLocation> updateStonesEnemyGroups(
    		Vector<StonesGroupOld> listOfNeighborEnemies) {

//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check entering updateStonesEnemyGroups");
        Vector<GobanLocation> changedLocations = new Vector<GobanLocation>();

        Enumeration e = listOfNeighborEnemies.elements();
        for(;e.hasMoreElements();) {
            StonesGroupOld currentGroup = (StonesGroupOld)e.nextElement();
            if (currentGroup.hasNoLiberty()) {
                    // update the captured stones info
                if (currentGroup.getColor() == IBoard.BLACK_STONE) { 
                	nrBlackPrisoners += currentGroup.nrStones(); 
                }
                else { 
                	nrWhitePrisoners += currentGroup.nrStones(); 
                }

                    // add the stones of this group to the list of changed locations
                changedLocations.addAll(currentGroup.getStones());

                    // iterate for all the stones in the "to be captured" enemy group
                Enumeration stonesList = currentGroup.getStones().elements();
                for(;stonesList.hasMoreElements();) {
                    GobanLocation currentStone = (GobanLocation)stonesList.nextElement();

                        // update the liberties after removing currentStone
                    Enumeration enemyList = getNeighborEnemies(currentStone).
                        elements();

                    for (;enemyList.hasMoreElements();) {
                        StonesGroupOld currentEnemy = (StonesGroupOld)enemyList.nextElement();
                        currentEnemy.getLiberties().add(currentStone);
                    }

                        // we actually remove the currentStone here
                    currentStone.state = IBoard.NO_STONE;

                    if (currentGroup.getColor() == IBoard.BLACK_STONE)
                        blackGroups.remove(currentGroup);
                    else
                        if (currentGroup.getColor() == IBoard.WHITE_STONE)
                            whiteGroups.remove(currentGroup);
                } // for
            } // if
        } // for

//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check exiting updateStonesEnemyGroups");

        return changedLocations;

    } //updateStonesEnemyGroups


        // fill the lists with stone's friend and enemy groups
    public void visitNewStoneNeighbors(GobanLocation stone,
    		Vector<StonesGroupOld> listOfNeighborFriends,
    		Vector<StonesGroupOld> listOfNeighborEnemies) {

        if (stone.x > 0) {
            int currentX = stone.x - 1;
            int currentY = stone.y;

            processNeighbor(stone,
                            currentX, currentY,
                            listOfNeighborFriends,
                            listOfNeighborEnemies);
        }

        if (stone.y > 0) {
            int currentX = stone.x;
            int currentY = stone.y - 1;

            processNeighbor(stone,
                            currentX, currentY,
                            listOfNeighborFriends,
                            listOfNeighborEnemies);
        }

        if (stone.x < size - 1) {
            int currentX = stone.x + 1;
            int currentY = stone.y;

            processNeighbor(stone,
                            currentX, currentY,
                            listOfNeighborFriends,
                            listOfNeighborEnemies);
        }

        if (stone.y < size - 1) {
            int currentX = stone.x;
            int currentY = stone.y + 1;

            processNeighbor(stone,
                            currentX, currentY,
                            listOfNeighborFriends,
                            listOfNeighborEnemies);
        }
    } // visitStoneNeighbors


    private void newBornGroup(GobanLocation stone) {
        StonesGroupOld newGroup = new StonesGroupOld(stone.state);
        newGroup.add(stone);
        stone.setGroup(newGroup);

            //add this group to the appropriate list of groups
            //System.out.println("[GoPosition] Add the new group to the list of groups");
        if (newGroup.getColor() == IBoard.BLACK_STONE) {
                //System.out.println("It's the Black group " + newGroup + " to add to list " + blackGroups);
                //System.out.println("Position is now " + this + " and blackGroupList: " + this.blackGroups);
            blackGroups.add(newGroup);
        }
        else {
                //System.out.println("It's the White group " + newGroup + " to add to list " + whiteGroups);
            whiteGroups.add(newGroup);
        }
    }


        // adding a stone to the group (theGroup)
    private void addStoneToGroup(GobanLocation stone,
                                 StonesGroupOld theGroup) {
            //System.out.println("[addStoneToGroup] We have one friend: the group " + theGroup);

            // add the stone to the group's list
        theGroup.getStones().add(stone);

            //System.out.println("Setting the stone's 'group' property...");
        stone.setGroup(theGroup);

        theGroup.getLiberties().addAll(stone.getLiberties());
    } // addStoneToOneGroup



        // the last move (stone) is connected with several friendly groups (listOfNeighborFriends)
    private void connectMoreGroups(GobanLocation stone,
    		Vector<StonesGroupOld> listOfNeighborFriends) {
            //System.out.println("[connectMoreGroups]We have more friends:" + listOfNeighborFriends);
            // get a handle of the first friend group in the list
        StonesGroupOld theGroup = listOfNeighborFriends.firstElement();

            //theGroup.liberties.appendList(stone.liberties);

            // the other friend groups in the list will be embeded in the first one
        Enumeration e = listOfNeighborFriends.elements();
        e.nextElement();

            //System.out.println("[connectMoreGroups]entered for loop");
        for (;e.hasMoreElements();) {


                // get a handle of the next friend group in the list
            StonesGroupOld group = (StonesGroupOld)e.nextElement();

            // change the apartenence of the stones in the group to be "swalowed"
            for (Enumeration e2 = group.getStones().elements(); e2.hasMoreElements();) {
              GobanLocation currentStone = (GobanLocation)e2.nextElement();
              currentStone.setGroup(theGroup);
            }


            // swallow the liberties and the stones of one group into the other
            theGroup.getLiberties().addAll(group.getLiberties());
            theGroup.getStones().addAll(group.getStones());

            // delete the second group from the coresponding group list
            if (stone.state == IBoard.BLACK_STONE)
                blackGroups.remove(group);
            else
                whiteGroups.remove(group);
        }
        
        addStoneToGroup(stone, theGroup);
    }

    private Set getNeighborsList(GobanLocation point) {
//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check entering getNeighborsList");

        Set listOfNeighbors = new HashSet();

        if (point.x > 0) {
            listOfNeighbors.add(this.goban[point.x - 1][point.y]);
        }

        if (point.y > 0) {
            listOfNeighbors.add(this.goban[point.x][point.y - 1]);
        }

        if (point.x < size - 1) {
            listOfNeighbors.add(this.goban[point.x + 1][point.y]);
        }

        if (point.y < size - 1) {
            listOfNeighbors.add(this.goban[point.x][point.y + 1]);
        }

//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check exitin getNeighborsList");
        return listOfNeighbors;
    }

        /**
         * Updates the "liberties" list for this GobanLocation
         */
    private void processLiberty(GobanLocation point,
                                int currentX,
                                int currentY) {
    	GobanLocation currentNeighbor = this.goban[currentX][currentY];
        if (currentNeighbor.state == IBoard.NO_STONE)
            point.addLiberty(currentNeighbor);

//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    private void computeLiberties(GobanLocation point) {
        Set listOfNeighbors = getNeighborsList(point);
        Iterator e = listOfNeighbors.iterator();
        for (;e.hasNext();) {
            GobanLocation currentNeighbor = (GobanLocation)e.next();
            processLiberty(point, currentNeighbor.x, currentNeighbor.y);
        }

//        if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    public Vector<StonesGroupOld> getNeighborEnemies(GobanLocation point) {
        Set listOfNeighbors = this.getNeighborsList(point);
        Vector<StonesGroupOld> listOfEnemyGroups = new Vector<StonesGroupOld>();

        Iterator e = listOfNeighbors.iterator();
        for (;e.hasNext();) {
            GobanLocation currentNeighbor = (GobanLocation)e.next();
            if (currentNeighbor.state != IBoard.NO_STONE  ||
                currentNeighbor.state != point.state)
                listOfEnemyGroups.add(currentNeighbor.getGroup());
        }

        return listOfEnemyGroups;
    }

        /**
     * Sets the liberties for this GobanLocation,
     * knowing the goban is all empty.
     */
    private void setEmptyGobanLiberties(GobanLocation point) {
       	if (point.x > 0) {
          point.addLiberty(goban[point.x - 1][point.y]);
        }

        if (point.y > 0) {
          point.addLiberty(goban[point.x][point.y - 1]);
        }

        if (point.x < size - 1) {
          point.addLiberty(goban[point.x + 1][point.y]);
        }

        if (point.y < size - 1) {
          point.addLiberty(goban[point.x][point.y + 1]);
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    public static int opposite( int color ) {
        if (color != IBoard.BLACK_STONE && color != IBoard.WHITE_STONE) {
            System.err.println("GoPosition.opposite called with illegal argument");
            System.exit( 1 );
        }

        return ( color == IBoard.BLACK_STONE ) ?
        		IBoard.WHITE_STONE :
        			IBoard.BLACK_STONE;
    }

    public void emptyGoban() {
        history = new Vector();

        for (int i = 0; i < size; i++) {
            for (int k = 0; k < size; k++) {
                this.goban[i][k].state = IBoard.NO_STONE;
                    // in order to refresh the "liberties" list on game replay
                this.computeLiberties(goban[i][k]);
            }
        }

        //if (Debug.isDebugOn()) Debug.assert(isConsistent(), "ERROR: GoPosition consistency check");
    }

    public int getPointColor( int x, int y ) {
        return goban[x][y].state;
    }

    public Vector getHistory() {
        return history;
    }

        // Consistency self-checking.
    public boolean isConsistent() {
/*
        int numBlack = 0;
        int numWhite = 0;

        for (int x = 0; x < size; ++x) {
            for (int y = 0; y < size; ++y) {
                if (goban[x][y].state == GoConstants.BLACK) {
                    numBlack++;
                }

                if (goban[x][y].state == GoConstants.WHITE) {
                    numWhite++;
                }
            }
        }

        numBlack += nrBlackPrisoners;
        numWhite += nrWhitePrisoners;


        int diff = numBlack - numWhite;
        if ( diff != 1 && diff != 0 ) {
            System.err.println("GoPosition.isConsistent failed: unbalanced number of stones: " + numBlack + " for BLACK and " + numWhite + " for WHITE");
            return false;
        }

        if (history_ == null) {
            for (int x = 0; x < size; ++x) {
                for (int y = 0; y < size; ++y) {
                    if (!isEmpty( x, y )) {
                        System.err.println("GoPosition.isConsistent failed: no history, non-empty board");
                        return false;
                    }
                }
            }
        } else {
            if (history_.size() != numBlack + numWhite) {
                System.err.println("GoPosition.isConsistent failed: history doesn't match board: " + this + " numBlack = " + numBlack + " numWhite = " + numWhite);
                return false;
            }

            boolean notStarted = true;
            int prev = GoConstants.EMPTY;
            for (int i = 0; i < history_.size(); ++i) {
                Move move = (Move)history_.elementAt(i);

                if (move.color != GoConstants.BLACK &&
                    move.color != GoConstants.WHITE) {
                    System.err.println("GoPosition.isConsistent failed: illegal move in history: " + move);
                    return false;
                }

                if (notStarted) {
                    notStarted = false;
                    prev = move.color;
                } else {
                    if ( prev == move.color ) {
                        System.err.println("GoPosition.isConsistent failed: attempt to move twice in a row with same color: " + prev + " and " + move);
                        return false;
                    }
                    prev = move.color;
                }
            }
        }
*/
        return true;
    }

    private void addToHistory(Move move) {
        if (history.size() > 0) {
            //if (Debug.isDebugOn()) Debug.assert( (move.color != ((Move)history_.lastElement()).color), "error adding " + move + " to " + this);
        }

        history.addElement(move);
    }



            // goban size
    private int size;

        //list of black & white groups
    private Vector<StonesGroupOld> blackGroups;
    private Vector<StonesGroupOld> whiteGroups;

        // number of captured Black stones
    private int nrBlackPrisoners = 0;

        // number of captured White stones
    private int nrWhitePrisoners = 0;

        // list of Moves
    private Vector<Move> history = new Vector<Move>();

            // all the points on the goban
    private GobanLocation[][] goban;

}
